package main

import (
	"flag"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
)

type Client struct {
	ServerIp   string
	ServerPort int
	Name       string
	conn       net.Conn
	flag       int // 记录当前客户端的聊天模式
}

// 创建客户端的方法
func NewClient(serverIp string, serverPort int) *Client {
	// 创建客户端对象
	client := &Client{
		ServerIp:   serverIp,
		ServerPort: serverPort,
		flag:       999,
	}

	// 连接Server
	conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))
	if err != nil {
		fmt.Println("net.Dial error:", err)
		return nil
	}
	client.conn = conn

	// 返回对象
	return client
}

func (this *Client) menu() bool {
	fmt.Println("1:群聊模式")
	fmt.Println("2:私聊模式")
	fmt.Println("3:更新用户名")
	fmt.Println("0:退出")

	var flag int
	fmt.Scanln(&flag) // 从用户键盘获得输入，并赋值给flag
	if flag >= 0 && flag <= 3 {
		this.flag = flag
		return true
	} else {
		fmt.Println("青输入合法范围内的数字")
		return false
	}
}

// 修改用户名
func (this *Client) UpdateName() bool {
	fmt.Println("======请输入用户名：======")
	fmt.Scanln(&this.Name) // 将控制台输入的用户名直接赋值给this.Name
	sendMsg := "rename|" + this.Name
	_, err := this.conn.Write([]byte(sendMsg))
	if err != nil {
		fmt.Println("this.conn.Write err:", err)
		return false
	}
	return true
}

// 群聊
func (this *Client) PublicChat() {
	fmt.Println("======请输入聊天内容，exit退出：======")
	var chatMsg string

	fmt.Scanln(&chatMsg)
	for chatMsg != "exit" {
		if len(chatMsg) != 0 {
			_, err := this.conn.Write([]byte(chatMsg + "\n"))
			if err != nil {
				fmt.Println("this.conn.Write err:", err)
				break
			}
		}
		chatMsg = ""
		fmt.Println("======请输入聊天内容：======")
		fmt.Scanln(&chatMsg)
	}
}

// 查询当前在线用户
func (this *Client) SelectUsers() {
	sendMsg := "w"
	_, err := this.conn.Write([]byte(sendMsg))
	if err != nil {
		fmt.Println("SelectUsers err:", err)
	}
}

// 私聊模式
func (this *Client) PrivateChat() {
	var remoteName string
	var chatMsg string

	this.SelectUsers() // 先查询一下当前在线用户
	fmt.Println("======请输入聊天对象，exit退出：======")

	fmt.Scanln(&remoteName)

	// 一次exit只可以退出一层for循环
	for remoteName != "exit" {
		fmt.Println("======请输入聊天内容，exit退出：======")
		fmt.Scanln(&chatMsg)
		for chatMsg != "exit" {
			if len(chatMsg) != 0 {
				remoteMsg := "to|" + strings.TrimSpace(remoteName) + "|" + chatMsg
				fmt.Println("remoteMsg=", remoteMsg)
				_, err := this.conn.Write([]byte(remoteMsg + "\n"))
				if err != nil {
					fmt.Println("this.conn.Write err:", err)
					break
				}
			}
			chatMsg = ""
			fmt.Println("======请输入聊天内容，exit退出：======")
			fmt.Scanln(&chatMsg)
		}

		this.SelectUsers() // 先查询一下当前在线用户
		fmt.Println("======请输入聊天对象，exit退出：======")

		fmt.Scanln(&remoteName)
	}

}

var serverIp string
var serverPort int

// 从命令行flag中获得输入的变量
// 格式：client.exe -ip 127.0.0.1 -port 8888
func init() {
	// 解释：从flag命令行中解析出ip参数，赋值给serverIp这个变量
	flag.StringVar(&serverIp, "ip", "127.0.0.1", "设置服务器IP地址（默认127.0.0.1）")
	flag.IntVar(&serverPort, "port", 8888, "设置服务器IP地址（默认8888）")
}

// 主业务功能
func (this *Client) Run() {
	for this.flag != 0 {
		for this.menu() != true {
			// 如果menu()返回值不为true，则一直空循环
		}

		switch this.flag {
		case 1:
			fmt.Println("1:群聊模式")
			this.PublicChat()
			break
		case 2:
			fmt.Println("2:私聊模式")
			this.PrivateChat()
			break
		case 3:
			fmt.Println("3:更新用户名")
			this.UpdateName()
			break
			// 为0时，会在外围的循环中跳出
		}
	}
}

func main() {
	// 解析命令行
	flag.Parse()

	client := NewClient(serverIp, serverPort)
	if client == nil {
		fmt.Println("连接服务器失败")
	}

	fmt.Println("=======连接服务器成功======")

	// 单独开启一个goroutine去处理server的回执消息
	go client.DealResponse()

	client.Run()
}

// 专门用来处理服务器返回值的goroutine
func (this *Client) DealResponse() {
	io.Copy(os.Stdout, this.conn) // 将this.conn中的流直接拷贝到标准输出os.Stdout，永久阻塞监听

	// 等价于下面的写法
	//for {
	//    buf := make([]byte, 4096)
	//    this.conn.Read(buf)
	//    fmt.Println(string(buf))
	//}
}
